"""
    apps.accounts.views
    ===================

    Manages account login/logout, signup and general account actions.

    Uses ARM (libs.adrianremembersme) models.
"""

import urllib, re , hashlib, datetime, logging

from google.appengine.api import mail
from google.appengine.ext import db

from django                   import forms
from django.conf              import settings as djangosettings
from django.http              import HttpResponse , HttpResponseForbidden, HttpResponseBadRequest, HttpResponseServerError
from django.core.urlresolvers     import reverse
from django.template.loader       import render_to_string
from django.utils.translation     import ugettext as _
from django.views.decorators.http import require_http_methods

from django.views.generic.simple import direct_to_template
from django.shortcuts            import render_to_response, redirect

from libs.adrianremembersmeapp            import AdrianRemembersMe , armsettings
from libs.adrianremembersmeapp.forms      import LoginForm, SignupForm , ForgotPasswordForm , ResetPasswordForm , ChangePasswordForm , ChangeImageForm
from libs.adrianremembersmeapp.decorators import login_required, login_not_required

def mailnotify(subject, to,
        template_txt="", template_html=None, template_vars=dict()):
    """
        Helper function to send email to people.

        You can pass an `html_template`, `txt_template` or both.
        If both, a multipart email is sended with defaults to the html part.

        However, since is multipart, users who do not have html mail clients will
        see the text email.
    """
    sender_address = djangosettings.DEFAULT_FROM_EMAIL
    if sender_address == "webmaster@localhost":
        host = request.get_host()
        sender_address = 'no-reply@' + host.replace('appspot', 'appspotmail')

    # HTML is prefered over text
    if template_html:
        bodyhtml   = render_to_string(template_html, template_vars )

    if template_txt:
        bodytxt    = render_to_string( template_txt, template_vars )

    mail.send_mail(sender=sender_address, to=to, subject=subject, body=bodytxt, html=bodyhtml)
    return (bodyhtml or bodytxt)


#########################################
################ views ##################
#########################################

def profile(request, member=None):
    """ Render the public profile of given person.

        .. note:: This is the new code for uploadapp.profile view, but i think
                  we should separate the database access.
    """
    arm            = AdrianRemembersMe()
    profile_member = (member and arm.get(member) or request.member)

    #if not request.member:
    #    return HttpResponseBadRequest(_("Invalid or inexistent user."))

    return direct_to_template( request, 'accounts/profile.html' , {'owned_projects': [], 'all_projects': [], 'profile_member': profile_member})

@login_required
def settings(request):
    """ Render the available settings """
    return direct_to_template( request, 'accounts/settings.html')


def login(request):
    """ Login an user """
    login_form = LoginForm()
    next_url   = request.GET.get('next', reverse('account_profile') )
    #next_url   = request.GET.get('next', '/' ) # alternative next_url: '/projectscreate'

    if request.POST:

        login_form = LoginForm(request.POST, request=request)
        if login_form.is_valid():

            # Login the user!
            member     = login_form.cleaned_data['member']
            rememberme = login_form.cleaned_data['rememberme']
            return AdrianRemembersMe().login( member, rememberme=rememberme , response=redirect(next_url))

    return direct_to_template( request, 'accounts/login.html',
                               {'login_form': login_form,
                                'redirect_to': next_url})


def logout(request):
    """ Logout user and clear auth cookie """
    AdrianRemembersMe().logout(cookie=request.member_cookie)

    response = redirect("/")
    response.delete_cookie('user')

    return response


def signup(request, invite_code=None):
    """ Signup a new user """
    
    signup_form = SignupForm()
    if request.POST:
        signup_form = SignupForm(request.POST, request.FILES)

        if signup_form.is_valid():
            username , password = signup_form.cleaned_data.get('username') , signup_form.cleaned_data.get('password')
            email    , name     = signup_form.cleaned_data.get('email') ,    signup_form.cleaned_data.get('name')
            picture  = signup_form.cleaned_data['picture'] or {}
            logging.info(picture)
            arm    = AdrianRemembersMe()
            try:
                # Create the user
                member, email_confirm_token = arm.signup( username, password, name , email, photo=picture )
                request.member              = member
		#logging.info("Member after arm.signup(): %s", member)
                # Send a email validation
                validate_email_url = 'http://%s%s' % (request.get_host(),
                                                reverse('account_validate_email', args=[email_confirm_token]) )
                mailnotify( _("[CoderBuddy] Validate your email"), email,
                            template_html = "accounts/emails/validate_email.html",
                            template_txt  = "accounts/emails/validate_email.txt",
                            template_vars = {'validate_email_url': validate_email_url,'member': member} )

                # Log him if email confirmation is not necessary.
                #if not armsettings.USE_MAIL_CONFIRMATION:
                redirect_to = redirect( reverse('account_profile') )
                
                return arm.login( member, rememberme=False, response=redirect_to)

            except forms.ValidationError:
                # Uhmn, shouldn't happen this(since data is already validated!), let's return an Http500
                return HttpResponseServerError()

    return direct_to_template( request, 'accounts/signup.html', {'signup_form': signup_form})

def forgot_password(request):
    """
    Let the user retrieve his password.

    .. note:: Not enabled if `armsettings.USE_MAIL_CONFIRMATION` is disabled.
    """
    if not armsettings.USE_MAIL_CONFIRMATION:
        return HttpResponseForbidden(djangosettings.DEBUG and _("Please enable armsettings.USE_MAIL_CONFIRMATION"))

    form = ForgotPasswordForm((request.method == "POST" and request.POST or None))
    if form.is_bound and form.is_valid():
        email      = form.cleaned_data.get('email')
        reset_info = AdrianRemembersMe().forgot_password( email )

        # Reset password URL
        reset_pwd_url = 'http://%s%s' % (request.get_host(),
                                            reverse('account_password_reset', args=[reset_info.get('token')]) )

        # Send Email with the reset link
        mailnotify( _("[CoderBuddy] Password retrieval instructions"), email,
                    template_html = "accounts/emails/forgot_password.html",
                    template_txt  = "accounts/emails/forgot_password.txt",
                    template_vars = dict({'reset_url': reset_pwd_url}, **reset_info) )

        return direct_to_template(request,"accounts/forgot_password.html",{'email_resetpassword_sended': True} )

    return direct_to_template(request,"accounts/forgot_password.html", {'form': form})


def reset_password(request, reset_token):
    """
    Allow user to change password if he got an secret token.
    """
    arm    = AdrianRemembersMe()
    member = arm.retrieve_reset_token(reset_token)

    if not member:
        return HttpResponseBadRequest(_("Invalid or expired link. Please request a new one!"))

    reset_form = ResetPasswordForm((request.method == "POST" and request.POST or None))
    if reset_form.is_bound and reset_form.is_valid():
        new_password = reset_form.cleaned_data.get('new_password')

        # Block token, change password and login user
        arm.invalidate_reset_token( reset_token )
        arm.change_password( member, new_password )
        return arm.login(member, False, response=redirect( reverse('account_profile' ) ))

    return direct_to_template(request,"accounts/reset_password.html", {'form': reset_form, 'token': reset_token})


@login_required
def change_password(request):
    form = ChangePasswordForm( (request.method == "POST" and request.POST or None) , request=request)
    if form.is_bound and form.is_valid():
        new_password = form.cleaned_data.get('new_password')

        AdrianRemembersMe().change_password( request.member , new_password )
        return direct_to_template(request, "accounts/change_password.html", {'password_changed': True})

    return direct_to_template(request, "accounts/change_password.html", {'form': form})


@login_required
def change_image(request):
    form = ChangeImageForm( (request.method == "POST" and request.POST or None),
                            (request.method == "POST" and request.FILES or None))
    if form.is_bound and form.is_valid():
        photo       = form.cleaned_data['photo']
        valid_photo = AdrianRemembersMe().save_photo( request.member, photo.read(), photo.name )

        return direct_to_template(request, "accounts/change_image.html", {'image_changed': True})

    return direct_to_template(request, "accounts/change_image.html", {'form': form})


def validate_email(request, email_token):
    arm    = AdrianRemembersMe()
    member = arm.validate_emailcode(email_token)

    # TODO: Add RateLimiter
    if not member:
        return HttpResponseBadRequest(_("Invalid or expired link. Please request a new one!"))
    else:
        return arm.login( member, rememberme=False, response=redirect('/projectscreate') )

#######################################################
# Public things that doesn't need to be authenticated #
#######################################################


@require_http_methods(['GET',])
def public_image(request, is_thumb=False, username=''):
    """
    Get the public profile image of the given user or it's gravatar.

    .. note:: Profile images saved in coderbuddy have priority over gravatar, even if enabled.
    """
    user          = AdrianRemembersMe().get(username)
    t_def , n_def = armsettings.DEFAULT_THUMBNAIL , armsettings.DEFAULT_PROFILE_IMAGE
    t_siz , n_siz = armsettings.THUMBNAIL_SIZE , armsettings.PROFILE_IMAGE_SIZE

    if user:
        photo = user.photo_set.get()
        if photo:
            return (is_thumb and HttpResponse(photo.thumb, mimetype="image/png")
                            or HttpResponse(photo.content, mimetype="image/jpeg"))

        if armsettings.USE_GRAVATAR:
            logging.info('http://%s%s' % ( request.get_host() , (n_def)))
            return redirect( 'http://www.gravatar.com/avatar/%s?%s' % (
                              hashlib.md5(user.email).hexdigest(),
                              # s=size, r=rating, d=redirectto_if_notfound
                              urllib.urlencode({
                                's': (is_thumb and t_siz['width'] or n_siz['width']),
                                'r': 'g',
                                'd': 'http://%s%s' % ( request.get_host() , (is_thumb and t_def or n_def ))}))
                        )
        else:
            return redirect(n_def)

    # Ok, no user, returns a badrequest
    return HttpResponseBadRequest()

@require_http_methods(['GET',])
def is_available(request):
    """
       Check if email or username if available for signup.
       Returns a simple json true/false indicating the availability. This is due to
       jquery.validator plugins.

    """
    import simplejson as sjson 
    
    email, username = request.GET.get('email',None) , request.GET.get('username', None)
    
    arm           = AdrianRemembersMe()
    available     = (email and arm.email_available(email) or arm.username_available(username))
    
    json_response = HttpResponse()
    sjson.dump(available, json_response )
